package com.superquan.selfstudybootmybatis.utils.encrypt.base64;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.superquan.selfstudybootmybatis.entity.encrypt.KeyPairInfo;
import com.superquan.selfstudybootmybatis.utils.encrypt.SmsUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
* 
* @ClassName: SecurityUtils.java
* @author: zwq
* @Description: 安全相关工具箱
*
 */
@Slf4j
public class SafeUtils {
	
	/**
	 * Sha1加密
	 * @param inStr
	 * @return
	 */
	public static String shaEncode(String inStr) {
		log.info("sha1加密,字符串传入开始加密操作*****");
		log.info("inStr*****",inStr);
		try {
			MessageDigest sha = MessageDigest.getInstance("SHA1");
			byte[] byteArray = inStr.getBytes("UTF-8");
			byte[] md5Bytes = sha.digest(byteArray);
			StringBuffer hexValue = new StringBuffer();
			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16) {
					hexValue.append("0");
				}
				hexValue.append(Integer.toHexString(val));
			}
			return hexValue.toString().toUpperCase();
		} catch (Exception e) {
			log.error("SHA1字符串加密异常：", e);
			return "";
		}
	}
	
	/**
	 * SHA加密
	 * @param data 字节数组
	 * @return
	 */
	public static String shaByteEncode(byte[] data) {
		log.info("sha1加密,字节数组传入开始加密操作*****");
		log.info("data*****",data);
		try {
			MessageDigest sha = MessageDigest.getInstance("SHA1");
			byte[] md5Bytes = sha.digest(data);
			StringBuffer hexValue = new StringBuffer();
			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16) {
					hexValue.append("0");
				}
				hexValue.append(Integer.toHexString(val));
			}
			return hexValue.toString().toLowerCase();
		} catch (Exception e) {
			log.error("SHA1数组加密异常：", e);
			return "";
		}
	}
	
	
	/**
	 * sha256加密
	 * @param inStr 加密字符串
	 * @return
	 */
	public static String sha256ByteEncode(String inStr) {
		log.info("sha256加密,字符串传入开始加密操作*****");
		log.info("data*****",inStr);
		try {
			MessageDigest sha = MessageDigest.getInstance("SHA-256");
			byte[] byteArray = inStr.getBytes("UTF-8");
			byte[] md5Bytes = sha.digest(byteArray);
			StringBuffer hexValue = new StringBuffer();
			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16) {
					hexValue.append("0");
				}
				hexValue.append(Integer.toHexString(val));
			}
			return hexValue.toString().toLowerCase();
		} catch (Exception e) {
			log.error("SHA256数组加密异常：", e);
			return "";
		}
	}
	
	/**
	 * sha256加密
	 * @param data 字节数组
	 * @return
	 */
	public static String sha256ByteEncode(byte[] data) {
		log.info("sha256加密,字节数组传入开始加密操作*****");
		log.info("data*****",data);
		try {
			MessageDigest sha = MessageDigest.getInstance("SHA-256");
			byte[] md5Bytes = sha.digest(data);
			StringBuffer hexValue = new StringBuffer();
			for (int i = 0; i < md5Bytes.length; i++) {
				int val = ((int) md5Bytes[i]) & 0xff;
				if (val < 16) {
					hexValue.append("0");
				}
				hexValue.append(Integer.toHexString(val));
			}
			return hexValue.toString().toLowerCase();
		} catch (Exception e) {
			log.error("SHA256数组加密异常：", e);
			return "";
		}
	}
	
	
	
	
	
	/** 
     * DES加密
     *  
     * @param key
     *            加密密钥，长度为24字节 
     * @param srcStr
     *            被加密的数据缓冲区（源） 
     * @return 
     * 
     */
	public static String desEncode(String key,String srcStr) {
		log.info("DES加密开始******");
		log.info("key******",key);
		log.info("srcStr******",srcStr);
        try {
        	Security.addProvider(new BouncyCastleProvider());
            // 24字节的密钥  
    		byte[] tmpKeyBytes = key.getBytes();
    		byte[] keybyte = new byte[24];
    		if (tmpKeyBytes.length > keybyte.length){
    			System.arraycopy(tmpKeyBytes, 0, keybyte, 0, keybyte.length);
    		}
    		else{
    			System.arraycopy(tmpKeyBytes, 0, keybyte, 0, tmpKeyBytes.length);
    			for (int i = tmpKeyBytes.length; i < keybyte.length; i++){
    				keybyte[i] = (byte) 0xFF;
    			}
    		}
            // 生成密钥  
            SecretKey deskey = new SecretKeySpec(keybyte, "DESede");
            
            byte[] src = srcStr.getBytes();
           
            // 加密  
            Cipher c1 = Cipher.getInstance("DESede");  
            c1.init(Cipher.ENCRYPT_MODE, deskey);
            
            byte[] result = c1.doFinal(src);
            return DatatypeConverter.printHexBinary(result);  
        } catch (java.security.NoSuchAlgorithmException e1) {  
            e1.printStackTrace();  
        } catch (javax.crypto.NoSuchPaddingException e2) {  
            e2.printStackTrace();  
        } catch (Exception e3) {
            e3.printStackTrace();  
        }  
        return null;  
    }
	
	/** 
     * des解密 
     *  
     * @param key
     *            加密密钥，长度为24字节 
     * @param srcStr
     *            加密后的缓冲区 
     * @return  
     */
	public static String desDecrypt(String key,String srcStr) {  
		log.info("DES解密开始******");
		log.info("key******",key);
		log.info("srcStr******",srcStr);
        try {
        	Security.addProvider(new BouncyCastleProvider());
            // 24字节的密钥  
    		byte[] tmpKeyBytes = key.getBytes();
    		byte[] keybyte = new byte[24];
    		if (tmpKeyBytes.length > keybyte.length){
    			System.arraycopy(tmpKeyBytes, 0, keybyte, 0, keybyte.length);
    		}else{
    			System.arraycopy(tmpKeyBytes, 0, keybyte, 0, tmpKeyBytes.length);
    			for (int i = tmpKeyBytes.length; i < keybyte.length; i++){
    				keybyte[i] = (byte) 0xFF;
    			}
    		}
        	
            // 生成密钥  
            SecretKey deskey = new SecretKeySpec(keybyte, "DESede");  
  
            // 解密  
            Cipher c1 = Cipher.getInstance("DESede");  
            c1.init(Cipher.DECRYPT_MODE, deskey);
            
            String mySrc = (srcStr.trim().length() % 2 != 0)?("0" + srcStr) : srcStr;
            return new String(c1.doFinal(DatatypeConverter.parseHexBinary(mySrc)));  
        } catch (java.security.NoSuchAlgorithmException e1) {  
            e1.printStackTrace();  
        } catch (javax.crypto.NoSuchPaddingException e2) {  
            e2.printStackTrace();  
        } catch (Exception e3) {
            e3.printStackTrace();  
        } 
        return null;  
    } 
	
	
	
    /**
    * @param str 加密字符串
    * @return
    * @Description 
    * base64 加密  
     */
    public static String getBase64(String str) {  
    	log.info("base64加密开始*****");
    	log.info("str*****",str);
    	byte[] b = null;
		try {
			b = str.getBytes("utf-8");
			return Base64.getEncoder().encodeToString(b);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "";
		
    }  
  
    /**
    * @param str 解密字符串
    * @return
    * @throws
    * @Description base64 解密
    * 
     */
    public static String getFromBase64(String str) { 
    	log.info("base64解密开始*****");
    	log.info("str*****",str);
		try {
			byte[] byteArr=Base64.getDecoder().decode(str);
			String msg = new String(byteArr,"utf-8");
			return msg;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
        return "";  
    }
    
    
    /**
     * 
    * @param map 创建token参数
    * @param ttl 过期时间
    * @return
    * @throws
    * @Description RSA密钥对
    *
     */
    public static KeyPairInfo genRsaKeyPair(Map<String,String> map, Long ttl){
  		log.debug("开始生成RSA密钥对*********");
  		KeyPairInfo keyPairInfo = new KeyPairInfo();
  		JcaPEMWriter privateJw = null;
  		JcaPEMWriter publicJw = null;
  		try{
  			KeyPairGenerator mKeyPairGenerator = KeyPairGenerator.getInstance("RSA");
			mKeyPairGenerator.initialize(2048, new SecureRandom());
  			
  			KeyPair keypair = mKeyPairGenerator.generateKeyPair();
  			
  			PublicKey publicKey = keypair.getPublic();
  			PrivateKey privateKey = keypair.getPrivate();
  			
  			//公钥
  			StringWriter publicSw = new StringWriter();
  			publicJw = new JcaPEMWriter(publicSw);
  			publicJw.writeObject(publicKey);
  			publicJw.flush();
  			
  			//私钥
  			StringWriter privateSw = new StringWriter();
  			privateJw = new JcaPEMWriter(privateSw);
  			privateJw.writeObject(privateKey);
  			privateJw.flush();
  			
  			String createToken = createRsaToken(publicKey, privateKey, map,ttl);
			if(StringUtils.isBlank(createToken)){
				log.debug("生成rsa-token失败");
				return keyPairInfo;
			}
  			keyPairInfo.setPublicKey(publicSw.getBuffer().toString());
			keyPairInfo.setPrivateKey(privateSw.getBuffer().toString());
			keyPairInfo.setToken(createToken);
			return keyPairInfo;
  		}catch(Exception e){
  			log.debug("生成RSA秘钥对异常：",e);
  			return keyPairInfo;
  		}finally {
  			if(null!=privateJw){
  				try {
  					privateJw.close();
  				} catch (IOException e) {
  					e.printStackTrace();
  				}
  			}
  			if(null!=publicJw){
  				try {
  					publicJw.close();
  				} catch (IOException e) {
  					e.printStackTrace();
  				}
  			}
  		}
  	}
    
   /**
    * 
   * @param
   * @return
   * @throws
   * @Description RSA token
   *
    */
    public static String createRsaToken(PublicKey pubKey, PrivateKey privKey,Map<String,String> map, Long ttl) throws Exception {
    	RSAPublicKey publicKey = (RSAPublicKey) pubKey;
    	RSAPrivateKey privateKey = (RSAPrivateKey) privKey;
		// header Map
		if(null==map || map.isEmpty()){
			return null;
		}
        Map<String, Object> headerMap = new HashMap<String, Object>();
        headerMap.put("alg", "HS256");
        headerMap.put("typ", "JWT");
        JWTCreator.Builder jwt = JWT.create().withHeader(headerMap);
        // 计算 token 过期时间
        if(null!=ttl){
        	//当前毫秒数
        	long now = System.currentTimeMillis();
            long exp = now + ttl;
            jwt.withExpiresAt(new Date(exp));
        }
        for (Map.Entry<String, String> entry : map.entrySet()) {
            jwt.withClaim(entry.getKey(), entry.getValue());
        }
        return jwt.sign(Algorithm.RSA256(publicKey, privateKey));
	}
    
    
    /**
     * ECC密钥对
    * @param map 创建token参数
    * @param ttl 过期时间
    * @return
    * @throws
    * @Description 
    *
     */
    public static KeyPairInfo genEccKeyPair(Map<String,String> map, Long ttl){
		log.debug("开始生成ECC密钥对*********");
		JcaPEMWriter privateJw = null;
		JcaPEMWriter publicJw = null;
		KeyPairInfo keyPairInfo = new KeyPairInfo();
		try{
			Security.addProvider(new BouncyCastleProvider());
			KeyPairGenerator mKeyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
			//secp384r1/secp521r1
			ECGenParameterSpec ecgenParameterSpec = new ECGenParameterSpec("prime256v1");
			mKeyPairGenerator.initialize(ecgenParameterSpec, new SecureRandom());
			
			KeyPair keypair = mKeyPairGenerator.generateKeyPair();
			
			PublicKey publicKey = keypair.getPublic();
			PrivateKey privateKey = keypair.getPrivate();
			
			//公钥
			StringWriter publicSw = new StringWriter();
			publicJw = new JcaPEMWriter(publicSw);
			publicJw.writeObject(publicKey);
			publicJw.flush();
			
			//私钥
			StringWriter privateSw = new StringWriter();
			privateJw = new JcaPEMWriter(privateSw);
			privateJw.writeObject(privateKey);
			privateJw.flush();
			
			String createToken = createEccToken(publicKey, privateKey, map,ttl);
			if(StringUtils.isBlank(createToken)){
				log.debug("生成token失败");
				return keyPairInfo;
			}
			
			keyPairInfo.setPublicKey(publicSw.getBuffer().toString());
			keyPairInfo.setPrivateKey(privateSw.getBuffer().toString());
			keyPairInfo.setToken(createToken);
			return keyPairInfo;
		}catch(Exception e){
			log.debug("生成ECC秘钥对异常：",e);
			
			return keyPairInfo;
		}finally {
			if(null!=privateJw){
				try {
					privateJw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(null!=publicJw){
				try {
					publicJw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
    
    /**
     * ECC token
    * @param
    * @return
    * @throws
    * @Description 
    *
     */
    public static String createEccToken(PublicKey pubKey, PrivateKey privKey,Map<String,String> map, Long ttl) throws Exception {
		ECPublicKey publicKey = (ECPublicKey) pubKey;
		ECPrivateKey privateKey = (ECPrivateKey) privKey;
		// header Map
		if(null==map || map.isEmpty()){
			return null;
		}
        Map<String, Object> headerMap = new HashMap<>();
        headerMap.put("alg", "ES256");
        headerMap.put("typ", "JWT");
        JWTCreator.Builder jwt = JWT.create().withHeader(headerMap);
        // 计算 token 过期时间
        if(null!=ttl){
        	//当前毫秒数
        	long now = System.currentTimeMillis();
            long exp = now + ttl;
            jwt.withExpiresAt(new Date(exp));
        }
        for (Map.Entry<String, String> entry : map.entrySet()) {
            jwt.withClaim(entry.getKey(), entry.getValue());
        }
        return jwt.sign(Algorithm.ECDSA256(publicKey, privateKey));
	}

    /**
     * 生成apikey
     * @MethodName: genApiKey
     * @author yangshao
     * @return String
     */
    public static String genApiKey(){
    	return SmsUtils.genId();
    }
    
    /**
     * 生成apiSecret
     * @MethodName: genApiSecret
     * @author yangshao
     * @param apiKey
     * @return String
     */
    public static String genApiSecret(String apiKey){
    	return sha256ByteEncode(apiKey);
    }
   
    
    public static void main(String[] args) {
    	System.err.println(genApiKey());
    	System.err.println(genApiSecret(genApiKey()));
	}
	
}
